#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>

#include <xpmem.h>
#include <xpmem_test.h>
#include "mpi.h"


int main () {
    int testsize = 4096;
    MPI_Init( NULL, NULL);
    int comm_sz, my_rank;
    MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    int lock, fd;
    if (my_rank == 0) {
        if ((lock = open("/tmp/xpmem.lock", O_RDWR)) == -1) {
            perror("open xpmem.lock");
            return -1;
        }
        if ((fd = open("/tmp/xpmem.share", O_RDWR)) == -1) {
            perror("open xpmem.share");
            return -1;
        }
    }
    xpmem_segid_t xpmem_vaddr_seg;
    int *share;
    if (my_rank == 0) {
        share = (int*)mmap(0, TMP_SHARE_SIZE, PROT_READ|PROT_WRITE,
                MAP_SHARED, fd, 0);
        if (share == MAP_FAILED) {
            perror("mmap");
            return -1;
        }
        // memset(share, '\0', TMP_SHARE_SIZE);
        xpmem_vaddr_seg = make_share(&share, SHARE_SIZE);
        if (xpmem_vaddr_seg == -1) {
            perror("xpmem_make");
            return -1;
        }

        printf("[%d]: mypid = %d\n", my_rank, getpid());
        printf("[%d]: sharing %ld bytes\n", my_rank, SHARE_SIZE);
        printf("[%d]: segid = %llx at %p\n\n", my_rank, xpmem_vaddr_seg, share);
        // printf("[%d]: xpmem vaddr segid is: %llx\n", my_rank, xpmem_vaddr_seg);
    }
    printf("[%d]: Init finished\n", my_rank);
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Bcast((void*)((&xpmem_vaddr_seg)), 1, MPI_LONG_LONG, 0, MPI_COMM_WORLD);
    MPI_Barrier(MPI_COMM_WORLD);
    printf("[%d]: now get xpmem_vaddr_segid is： %llx\n", my_rank, xpmem_vaddr_seg);
    if (my_rank != 0) {
        int *data;
        xpmem_apid_t apid;
        data = (int*)attach_segid(xpmem_vaddr_seg, &apid);
        if (data == (int*)-1) {
            printf("[%d]: Yizimi's test failed!\n", my_rank);
            return -2;
        }
        int ret = 0;
        printf("[%d]: mypid = %d\n", my_rank, getpid());
	    printf("[%d]: segid = %llx\n", my_rank, xpmem_vaddr_seg);
	    printf("[%d]: attached at %p\n", my_rank, data);
        printf("[%d]: adding 1 to all elems\n\n", my_rank);
        for (int i = 0; i < testsize; i++) {
		    if (*(data + i) != i) {
                printf("xpmem_proc2: ***mismatch at %d: expected %d "
                        "got %d\n", i, i, *(data + i));
                ret = -2;
            }
            *(data + i) += 1;
        }
        xpmem_detach(data);
        xpmem_release(apid);

    }
    // printf("my_rank is %d\n", my_rank);
    MPI_Barrier(MPI_COMM_WORLD);
    if (my_rank == 0) {
        printf("[%d]: verifying data...", my_rank);
        int ret = 0;
        for (int i = 0; i < testsize; i++) {
            if (*(share + i) != i + 1) {
                printf("[%d]: ***mismatch at %d: expected %d "
                    "got %d\n", my_rank, i, i + 1, *(share + i));
                ret = -1;
            }
        }
        printf("done\n");

        unmake_share(xpmem_vaddr_seg, share, SHARE_SIZE);
    }
	munmap(share, TMP_SHARE_SIZE);
    MPI_Finalize();
    return 0;
}